package com.videri.match.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.videri.match.client.ChatMessageList;
import com.videri.match.client.VideriChatService;
import java.util.*;
import javax.servlet.*;

/**
 * This class will handle all functionality necessary for managing an SRM.
 * It's two major purposes are to handle users' movements between different
 * rooms of the SRM and to pass messages between different users.
 * 
 * @author Michael Bauer
 *
 */
public class VideriChatServlet extends RemoteServiceServlet implements
		VideriChatService {
	
	private static final long serialVersionUID = 8636411015070960931L;

	// This specifies the number of rooms that an SRM can have
	private final int NUM_ROOMS = 10;
	
	// This is the maximum number of characters that a message can contain
	private final int MAX_CHARS = 1000;
	
	// This is the tolerance for the minimum time between messages
	private final int MIN_TIME = 2000;
	
	// This is the name of the home chat room
	private final String HOME = "Home";
	
	// This is the string associated with the cookie attribute
	private final String COOKIE_ATTRIBUTE = "Servlet Cookies";
	
	private HashSet[] rooms;
	private HashMap messageQueues;
	private HashMap messageSourceQueues;
	private HashMap messageScopeQueues;
	private HashMap messageTiming;
	
	public String addMessage(String user, String message, String[] recipients) {
		if(rooms == null)
			initSRM();
		if(!messageQueues.containsKey(user))
			return "";
		if(message.length()>MAX_CHARS)
			return "Message length too long!";
		
		if(checkTiming(user)){
			leaveRoom(user);
			return "You are now being booted off the system for sending too many messages too quickly!";
		}
		
		// If recipients is empty then broadcast 
		if(recipients.length==0){
			Iterator it = messageQueues.keySet().iterator();
			while(it.hasNext()){
				String next = (String)it.next();
				if(next.equals(user))
					continue;
				List mess = (List)messageQueues.get(next);
				List source = (List)messageSourceQueues.get(next);
				List scope = (List)messageScopeQueues.get(next);
				mess.add(message);
				source.add(user);
				scope.add(new Boolean(false));
				messageQueues.put(next, mess);
				messageSourceQueues.put(next, source);
				messageScopeQueues.put(next, scope);
			}
			return "";
		}
		
		for(int i=0; i<recipients.length; i++){
			if(messageQueues.containsKey(recipients[i])){
				String cookie = getCookie(recipients[i]);
				List mess = (List)messageQueues.get(cookie);
				List source = (List)messageSourceQueues.get(cookie);
				List scope = (List)messageScopeQueues.get(cookie);
				mess.add(message);
				source.add(user);
				scope.add(new Boolean(true));
				messageQueues.put(cookie, mess);
				messageSourceQueues.put(cookie, source);
				messageScopeQueues.put(cookie, scope);
			}
		}
		
		return "";
	}

	public ChatMessageList getMessages(String user) {
		if(rooms == null)
			initSRM();
		if(messageQueues.containsKey(user)){
			List mess = (List)messageQueues.get(user);
			List sources = (List)messageSourceQueues.get(user);
			List scopes = (List)messageScopeQueues.get(user);
			if(mess.size()!=sources.size() || mess.size()!= scopes.size())
				System.err.println("Warning: Inconsistent queues!");
			String[] usernames = new String[sources.size()];
			String[] messages = new String[mess.size()];
			boolean[] priv = new boolean[scopes.size()];
			Iterator it1 = mess.iterator();
			Iterator it2 = sources.iterator();
			Iterator it3 = scopes.iterator();
			for(int i=0; i<usernames.length; i++){
				usernames[i] = getHandle((String)it2.next());
				messages[i] = (String)it1.next();
				priv[i] = ((Boolean)it3.next()).booleanValue();
			}
			ChatMessageList cml = new ChatMessageList(usernames, messages, priv);
			
			// Clear the users queues
			messageQueues.put(user, new LinkedList());
			messageSourceQueues.put(user, new LinkedList());
			messageScopeQueues.put(user, new LinkedList());
			
			return cml;
		}
		return null;
	}

	public String[] getUserList(String user, String room) {
		if(rooms == null)
			initSRM();
		int index = findRoomIndex(room);
		if(index>=0 && index<rooms.length){
			Set s = rooms[index];
			if(!s.contains(user))
				return null;
			String[] ret = new String[s.size()];
			//String[] ret = new String[s.size()-1];
			Iterator it = s.iterator();
			index=0;
			while(it.hasNext()){
				String cook = (String)it.next();
				//if(cook.equals(user))
				//	continue;
				ret[index++]=getHandle(cook);
			}
			return ret;
		}
		else{
			System.err.println("Warning: Invalid room number!");
		}
		return null;
	}

	public boolean joinRoom(String user, String room) {
		if(rooms == null)
			initSRM();
		
		String handle = getHandle(user);
		if(handle!=null && !handle.equals("") && getRoom(user)==null){
			int index = findRoomIndex(room);
			rooms[index].add(user);
			messageQueues.put(user, new LinkedList());
			messageSourceQueues.put(user, new LinkedList());
			messageScopeQueues.put(user, new LinkedList());
			return true;
		}
		return false;
	}

	public void leaveRoom(String user) {
		if(rooms == null)
			initSRM();
		
		String handle = getHandle(user);
		if(handle!=null && !handle.equals("")){
			int index = findRoomIndex(getRoom(user));
			if(index!=-1 && index>=0 && index<rooms.length){
				rooms[index].remove(user);
			}
		}	
	}
	
	public String getRoom(String user) {
		if(rooms==null)
			initSRM();
		
		String handle = getHandle(user);
		if(handle!=null && !handle.equals("")){
			for(int i=0; i<rooms.length; i++){
				if(rooms[i].contains(user))
					return getRoom(i);
			}
		}
		return null;
	}

	private void initSRM(){
		rooms = new HashSet[NUM_ROOMS+1];
		for(int i=0; i<=NUM_ROOMS; i++)
			rooms[i] = new HashSet();
		messageQueues = new HashMap();
		messageSourceQueues = new HashMap();
		messageScopeQueues = new HashMap();
		messageTiming = new HashMap();
	}
	
	// This method will return true if the user has been issuing too many messages
	private boolean checkTiming(String user){
		if(messageTiming.containsKey(user)){
			LinkedList times = (LinkedList)messageTiming.get(user);
			if(times.size()<5){
				times.add(new Double(System.currentTimeMillis()));
				messageTiming.put(user, times);
				return false;
			}
			else{
				times.removeFirst();
				times.add(new Double(System.currentTimeMillis()));
				double diff = ((Double)times.getLast()).doubleValue() - ((Double)times.getFirst()).doubleValue();
				if(diff<MIN_TIME){
					messageTiming.remove(user);
					return true;
				}
				messageTiming.put(user, times);
				return false;
			}
		}
		else{
			LinkedList times = new LinkedList();
			times.add(new Double(System.currentTimeMillis()));
			messageTiming.put(user, times);
			return false;
		}
	}
	
	private String getHandle(String cookie){
		Map cookies = getCookieMapping();
		if(cookies == null)
			return "Guest";
		VideriUser vu = (VideriUser)cookies.get(cookie);
		if(vu == null)
			return "Guest";
		return (String)vu.getHandle();
	}
	
	private String getCookie(String handle){
		Map cookies = getCookieMapping();
		Iterator it = cookies.keySet().iterator();
		while(it.hasNext()){
			String next = (String)it.next();
			String cook = (String)cookies.get(next);
			if(cook.equals(handle))
				return next;
		}
		return null;
	}
	
	private Map getCookieMapping(){
		ServletContext con = this.getServletContext();
		return (Map)con.getAttribute(COOKIE_ATTRIBUTE);
	}
	
	private int findRoomIndex(String room){
		if(room==null || room.equals(""))
			return -1;
		if(room.equals(HOME))
			return 0;
		return (new Integer(room.substring(room.lastIndexOf(" ")).trim())).intValue();
	}
	
	private String getRoom(int index){
		if(index<0 || index >= rooms.length)
			return "";
		if(index==0)
			return HOME;
		return "Room "+index;
	}
}
